package org.muxiao.seckill.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.commons.lang3.StringUtils;
import org.muxiao.seckill.mapper.OrderMapper;
import org.muxiao.seckill.pojo.*;
import org.muxiao.seckill.service.GoodsService;
import org.muxiao.seckill.service.OrderService;
import org.muxiao.seckill.service.SecKillGoodsService;
import org.muxiao.seckill.service.SecKillOrderService;
import org.muxiao.seckill.util.MD5Util;
import org.muxiao.seckill.util.UUIDUtil;
import org.muxiao.seckill.vo.GoodsVo;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.Date;
import java.util.concurrent.TimeUnit;

/**
 * 订单服务实现类
 */
@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {

    @Resource
    private SecKillGoodsService secKillGoodsService;

    @Resource
    private SecKillOrderService secKillOrderService;

    @Resource
    private GoodsService goodsService;

    @Resource
    private RedisTemplate redisTemplate;


    /**
     * 秒杀下单
     *
     * @param user
     * @param goodsVo
     * @return
     */
    @Transactional
    @Override
    public Order secKill(User user, GoodsVo goodsVo) {

        //更新秒杀商品库存
        SecKillGoods secKillGoods = secKillGoodsService.getOne(new QueryWrapper<SecKillGoods>().eq("goods_id", goodsVo.getId()));

        //1. Mysql在默认的事务隔离级[REPEATABLE-READ]别下
        //2. 执行update语句时,会在事务中锁定要更新的行
        //3. 这样可以防止其它会话在同一行执行update,delete
        System.out.println("------执行 update--->" + user.getId());
        boolean update = secKillGoodsService.update(new UpdateWrapper<SecKillGoods>()
                .setSql("stock_count=stock_count-1")
                .eq("goods_id", goodsVo.getId())
                .gt("stock_count", 0));

        if (!update) { //如果更新失败,说明已经没有库存
            return null;
        }

        //更新商品信息
        goodsService.update(new UpdateWrapper<Goods>()
                .setSql("goods_stock=goods_stock-1")
                .eq("id", goodsVo.getId())
                .gt("goods_stock", 0));

        //生成普通订单
        Order order = new Order();
        order.setUserId(user.getId());
        order.setGoodsId(goodsVo.getId());
        order.setDeliveryAddrId(0L);
        order.setGoodsName(goodsVo.getGoodsName());
        order.setGoodsCount(1);
        order.setGoodsPrice(secKillGoods.getSeckillPrice());
        order.setOrderChannel(1);
        order.setStatus(0);
        order.setCreateDate(new Date());

        this.save(order);

        //生成秒杀订单
        SecKillOrder secKillOrder = new SecKillOrder();
        secKillOrder.setUserId(user.getId());
        secKillOrder.setOrderId(order.getId());
        secKillOrder.setGoodsId(goodsVo.getId());

        secKillOrderService.save(secKillOrder);

        //将生成的秒杀订单，存入到Redis, 这样在查询某个用户是否已经秒杀了这个商品时，
        //直接到Redis中查询，起到优化效果
        //设计秒杀订单的key => order:用户id:商品id
        redisTemplate.opsForValue().set("order:" + user.getId() + ":" + goodsVo.getId(), secKillOrder);

        return order;
    }

    /**
     * 生成秒杀路径
     *
     * @param user
     * @param goodsId
     * @return
     */
    @Override
    public String createPath(User user, Long goodsId) {

        String path = MD5Util.md5(UUIDUtil.uuid());
        redisTemplate.opsForValue().set("secKillPath:" + user.getId() + ":" + goodsId, path, 60, TimeUnit.SECONDS);

        return path;
    }

    /**
     * 对秒杀路径进行校验
     *
     * @param user
     * @param goodsId
     * @param path
     * @return
     */
    @Override
    public boolean checkPath(User user, Long goodsId, String path) {

        if (user == null || goodsId < 0 || StringUtils.isBlank(path)) {
            return false;
        }

        String redisPath = (String) redisTemplate.opsForValue().get("secKillPath:" + user.getId() + ":" + goodsId);

        return path.equals(redisPath);
    }

    /**
     * 验证码校验
     *
     * @param user
     * @param goodsId
     * @param captcha
     * @return
     */
    @Override
    public boolean checkCaptcha(User user, Long goodsId, String captcha) {

        if (user == null || goodsId < 0 || StringUtils.isBlank(captcha)) {
            return false;
        }

        //从 redis 取出验证码
        String redisCaptcha = (String) redisTemplate.opsForValue().get("captcha:" + user.getId() + ":" + goodsId);

        return captcha.equals(redisCaptcha);
    }
}
